# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
import sys

if len(sys.argv) < 3:
    print "Too few arguments"
    sys.exit()

bound_args = int(sys.argv[1])
unbound_args = int(sys.argv[2])

def printCommentLine(comment):
    print "// %s %s" % (comment, '-' * (79 - len(comment) - 4))

arg_comment = [ 'no arguments', 'one argument', 'two arguments',
                'three arguments', 'four arguments', 'five arguments' ]

def param_list(prefix, name, count):
    return "".join(["%s%s%d, " % (prefix, name, i)
                    for i in range(1, count + 1)])
def typename(args):
    return "Closure%d" % (args,)

def full_typename(args):
    return "%s<%sR>" % (typename(args), param_list("", "P", args))


print """// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// This file was generated by the gen_closure.py script.

#pragma once

#include <memory>

namespace vespalib {
"""
printCommentLine("Interfaces")

for i in range(unbound_args + 1):
    print """
/** Interface of closures that take %s. */
template <%stypename R = void>
struct %s {
    typedef std::unique_ptr<%s > UP;

    virtual ~%s() {}

    virtual R call(%s) = 0;
};""" % (arg_comment[i], param_list('typename ', 'P', i),
         typename(i), full_typename(i), typename(i),
         ", ".join(["P%d param%d" % (j, j) for j in range(1, i + 1)]))
    if (i == 0):
        print 'typedef Closure0<void> Closure;'
print

for unbound in range(unbound_args + 1):
    printCommentLine("Closure%d" % unbound)
    for bound in range(bound_args + 1):
        print """
/** Implementation of %s binding %s to a free function. */
template <%s%stypename R>
class %s_%d : public %s {
    typedef R (*Func)(%s);

    Func _func;%s

    virtual R call(%s)
    { return _func(%s); }

public:
    %s_%d(Func func%s)
        : _func(func)%s {}
};""" % (typename(unbound), arg_comment[bound],
         param_list('typename ', 'T', bound),
         param_list('typename ', 'P', unbound),
         typename(unbound), bound, full_typename(unbound),
         ", ".join(['T%d' % i for i in range(1, bound + 1)] +
                   ['P%d' % i for i in range(1, unbound + 1)]),
         "".join(['\n    T%d _arg%d;' % (i, i) for i in range(1, bound + 1)]),
         ", ".join(["P%d param%d" % (i, i) for i in range(1, unbound + 1)]),
         ", ".join(["std::move(_arg%d)" % i for i in range(1, bound + 1)] +
                   ["std::move(param%d)" % (i) for i in range(1, unbound + 1)]),
         typename(unbound), bound,
         "".join([", T%d &&arg%d" % (i, i) for i in range(1, bound + 1)]),
         "".join([", _arg%d(std::move(arg%d))" % (i, i) for i in range(1, bound + 1)]))
    print

    printCommentLine("MemberClosure%d" % unbound)
    for bound in range(bound_args + 1):
        print """
/** Implementation of %s binding %s to a member function. */
template <class Ptr, class Obj, %s%stypename R>
class Member%s_%d : public %s {
    typedef R (Obj::*MemFun)(%s);

    Ptr _ptr;
    MemFun _mem_fun;%s

    virtual R call(%s)
    { return ((*_ptr).*_mem_fun)(%s); }

public:
    Member%s_%s(Ptr ptr, MemFun mem_fun%s)
        : _ptr(std::move(ptr)), _mem_fun(mem_fun)%s {}
};""" % (typename(unbound), arg_comment[bound],
         param_list('typename ', 'T', bound),
         param_list('typename ', 'P', unbound),
         typename(unbound), bound, full_typename(unbound),
         ", ".join(['T%d' % i for i in range(1, bound + 1)] +
                   ['P%d' % i for i in range(1, unbound + 1)]),
         "".join(['\n    T%d _arg%d;' % (i, i) for i in range(1, bound + 1)]),
         ", ".join(["P%d param%d" % (i, i) for i in range(1, unbound + 1)]),
         ", ".join(["std::move(_arg%d)" % i for i in range(1, bound + 1)] +
                   ["std::move(param%d)" % (i) for i in range(1, unbound + 1)]),
         typename(unbound), bound,
         "".join([", T%d arg%d" % (i, i) for i in range(1, bound + 1)]),
         "".join([", _arg%d(std::move(arg%d))" % (i, i) for i in range(1, bound + 1)]))
    print

for unbound in range(unbound_args + 1):
    printCommentLine("Function closures (%d)" % unbound)
    for bound in range(bound_args + 1):
        print """
/** Creates a %s from a free function, binding %s. */
template <%s%stypename R>
std::unique_ptr<%s >
makeClosure(R (*func)(%s)%s) {
    return std::unique_ptr<%s >(
            new %s_%d<%s%sR>(func%s));
}""" % (typename(unbound), arg_comment[bound],
       param_list('typename ', 'T', bound),
       param_list('typename ', 'P', unbound),
       full_typename(unbound),
       ", ".join(['T%d' % i for i in range(1, bound + 1)] +
                 ['P%d' % i for i in range(1, unbound + 1)]),
       "".join([", T%d arg%d" % (i, i) for i in range(1, bound + 1)]),
       full_typename(unbound),
       typename(unbound), bound,
       "".join(['T%d, ' % i for i in range(1, bound + 1)]),
       "".join(['P%d, ' % i for i in range(1, unbound + 1)]),
       "".join([", std::move(arg%d)" % (i) for i in range(1, bound + 1)])
       )
    print

    printCommentLine("Member closures (%d)" % unbound)
    for bound in range(bound_args + 1):
        print """
/** Creates a %s from a member function, binding %s. */
template <class Ptr, class Obj, %s%stypename R>
std::unique_ptr<%s >
makeClosure(Ptr ptr, R (Obj::*mem_fun)(%s)%s) {
    return std::unique_ptr<%s >(
            new Member%s_%d<Ptr, Obj, %s%sR>(
                    std::move(ptr), mem_fun%s));
}""" % (typename(unbound), arg_comment[bound],
       param_list('typename ', 'T', bound),
       param_list('typename ', 'P', unbound),
       full_typename(unbound),
       ", ".join(['T%d' % i for i in range(1, bound + 1)] +
                 ['P%d' % i for i in range(1, unbound + 1)]),
       "".join([", T%d arg%d" % (i, i) for i in range(1, bound + 1)]),
       full_typename(unbound),
       typename(unbound), bound,
       "".join(['T%d, ' % i for i in range(1, bound + 1)]),
       "".join(['P%d, ' % i for i in range(1, unbound + 1)]),
       "".join([", std::move(arg%d)" % (i) for i in range(1, bound + 1)])
       )
    print

print """}  // namespace vespalib

"""
